package com.huawei.java.alogrithm;

import com.huawei.java.model.*;

import java.util.*;

public class FullDistributionAlgorithm {
    public Graph graph;

    // 按照用户节点可以达到的边缘节点数量进行排序，能达到边缘节点数少的优先分配
    public TreeSet<ClientNode> clientNodeTreeSet;

    public List<Demand> demands;

    // key 用户 Map<String, Integer> 每个边缘节点给客户分配的带宽
    private final Map<String, Map<String, Integer>> allocationMaps = new HashMap<>();

    // 次数使用超过五次就踢出出set 边缘节点能达到用户数比较少的优先拉满这个边缘节点
    private TreeSet<SiteNode> siteNodeSet = new TreeSet<>((oneSite, anotherSite) -> {
        int result = oneSite.getValidClient().size() - anotherSite.getValidClient().size();
        return result == 0 ? 1 : result;
    });

    public FullDistributionAlgorithm(Graph graph, TreeSet<ClientNode> clientNodeTreeSet, List<Demand> demands) {
        this.graph = graph;
        this.clientNodeTreeSet = clientNodeTreeSet;
        this.demands = demands;
    }

    public void processCollection(float numDiv) {
        //时刻
        for (Demand demand : demands) {
            //记录本次时刻某个site 的总cost
            HashMap<String, Integer> siteCost = new HashMap<>();
            // 重新刷新结果集和带宽
            reloadBandwidth();
            Map<String, Integer> demandMap = demand.getDemandMap();

            //某一时刻的所有用户需求
            //先假设每次容量都能满足需求
            SiteNode selectSiteNode = null;
            int minCost = Integer.MAX_VALUE;
            int selectCost = Integer.MAX_VALUE;

            for (ClientNode clientNode : clientNodeTreeSet) {
                HashMap<String, Integer> allocationMap = new HashMap<>();

                int clientDemandBandwidth = demandMap.get(clientNode.getName());
                List<SiteNode> validSiteNodes = clientNode.getValidSiteNodes();
                siteNodeSet.clear();
                siteNodeSet.addAll(validSiteNodes);

                Map<SiteNode, Integer> fenbuAllocateMap = new HashMap<>();

                //先分配一次, 到前面的节点上，这样不增加成本，并且减少了其他节点以后的成本开销,调参保证影响自己的情况
                if (clientDemandBandwidth > 0 && (numDiv - 0) > 0.000001) {
                    for (SiteNode siteNode : siteNodeSet) {
                        if (clientDemandBandwidth > 0) {
                            int nowCost = AllocatedCollection.getCost(siteNode.getName());
                            if (!siteCost.containsKey(siteNode.getName())) {
                                //需要用到以往成本而不存在，先初始化为0
                                siteCost.put(siteNode.getName(), 0);
                            }
                            if (nowCost > 0 && siteCost.get(siteNode.getName()) < nowCost) {
                                int currentBandwidth = siteNode.getCurrentBandwidth();
                                int siteVaildAllocate = Math.min(currentBandwidth, clientDemandBandwidth);
                                int computecost = (int)((float)(nowCost - siteCost.get(siteNode.getName()))/numDiv);
                                siteVaildAllocate = Math.min(siteVaildAllocate, computecost);

                                clientDemandBandwidth -= siteVaildAllocate;
                                if (!fenbuAllocateMap.containsKey(siteNode)) {
                                    fenbuAllocateMap.put(siteNode, 0);
                                }
                                siteNode.setCurrentBandwidth(siteNode.getCurrentBandwidth() - siteVaildAllocate);
                                //本次用户需求中，某节点的总成本增加
                                if (!siteCost.containsKey(siteNode.getName())) {
                                    //需要用到以往成本而不存在，先初始化为0
                                    siteCost.put(siteNode.getName(), 0);
                                }
                                siteCost.put(siteNode.getName(), siteCost.get(siteNode.getName()) + siteVaildAllocate);
                                fenbuAllocateMap.put(siteNode, siteVaildAllocate + fenbuAllocateMap.get(siteNode));
                            }
                        }
                    }
                }

                //循环计算最小成本，取出应该选中的sitenode
                //之前只考虑了单个push的情况，新增场景
                //1 不能全部分配到一个节点的情况
                //2 雨露均沾充足分配
                boolean findCostZero = false;
                boolean findInsertLowCost = false;
                int minInsertPos = Integer.MAX_VALUE;
                Integer minChazhi = Integer.MAX_VALUE;
                for (SiteNode siteNode : siteNodeSet) {
                    if (clientDemandBandwidth <= 0) {
                        break;
                    }
                    int currentBandwidth = siteNode.getCurrentBandwidth();
                    if (currentBandwidth < clientDemandBandwidth) {
                        continue;
                    }
                    int siteValidAllocate = Math.min(currentBandwidth, clientDemandBandwidth);
                    if (!siteCost.containsKey(siteNode.getName())) {
                        //需要用到以往成本而不存在，先初始化为0
                        siteCost.put(siteNode.getName(), 0);
                    }
                    int preCost = siteCost.get(siteNode.getName()) + siteValidAllocate;
                    int changeCost = AllocatedCollection.computeCost(siteNode.getName(), preCost);
                    if (changeCost == 0) {
                        int insertpos = AllocatedCollection.insertPos(siteNode.getName(), preCost);
                        if (insertpos < minInsertPos) {
                            selectSiteNode = siteNode;
                            selectCost = siteValidAllocate;
                            minInsertPos = insertpos;
                        }

                        if (insertpos == minInsertPos) {

                            Integer cha = AllocatedCollection.chazhi(siteNode.getName(), preCost);
                            if (cha < minChazhi) {
                                selectSiteNode = siteNode;
                                selectCost = siteValidAllocate;
                                minChazhi = cha;
                            }

                        }

                        findCostZero = true;

                    }
                }

                //找到cost为0的节点
                if (selectSiteNode != null && findCostZero) {
                    selectSiteNode.setCurrentBandwidth(selectSiteNode.getCurrentBandwidth() - selectCost);
                    //本次用户需求中，某节点的总成本增加
                    siteCost.put(selectSiteNode.getName(), siteCost.get(selectSiteNode.getName()) + selectCost);
                    allocationMap.put(selectSiteNode.getName(), selectCost);
                    //OptimizeResult optimizeResult = new OptimizeResult();
                    //optimizeResult.redistribution(allocationMap, graph);

                    for (Map.Entry<SiteNode, Integer> entry : fenbuAllocateMap.entrySet()) {
                        SiteNode siteNode = entry.getKey();
                        allocationMap.put(siteNode.getName(), allocationMap.getOrDefault(siteNode.getName(), 0) + entry.getValue());
                    }

                    allocationMaps.put(clientNode.getName(), allocationMap);
                    Integer all = 0;
                    for (Map.Entry<String, Integer> entry : allocationMap.entrySet()) {
                        if (entry.getValue() > graph.getSiteIdMap().get(entry.getKey()).getTotalBandwidth())
                            System.out.println(clientNode.getName() + "erorrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr");
                        all += entry.getValue();
                    }

                    if (!all.equals(demandMap.get(clientNode.getName()))) {
                        System.out.println(clientNode.getName() + "erorrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr");
                    }
                    continue;
                }

                //找不到为0的，可能有两种，1是找到了单体插入的最小成本，2是找不到单体插入，只能分布插入
                //开始分布插入



                for (SiteNode siteNode : siteNodeSet) {
                    if (clientDemandBandwidth <= 0) {
                        break;
                    }
                    if (!siteCost.containsKey(siteNode.getName())) {
                        //需要用到以往成本而不存在，先初始化为0
                        siteCost.put(siteNode.getName(), 0);
                    }

                    //当前容量，用户需求，能添加的容量 取最小值
                    int currentBandwidth = siteNode.getCurrentBandwidth();
                    int siteVaildAllocate = Math.min(currentBandwidth, clientDemandBandwidth);
                    siteVaildAllocate = Math.min(siteVaildAllocate, AllocatedCollection.maxCanAddCost(siteNode.getName(), siteCost.get(siteNode.getName())));
                    //获取的都是造成成本增加0的情况，记录下可以分配的情况
                    if (siteVaildAllocate > 0) {
                        siteNode.setCurrentBandwidth(siteNode.getCurrentBandwidth() - siteVaildAllocate);
                        //本次用户需求中，某节点的总成本增加
                        siteCost.put(siteNode.getName(), siteCost.get(siteNode.getName()) + siteVaildAllocate);
                        if (!fenbuAllocateMap.containsKey(siteNode)) {
                            fenbuAllocateMap.put(siteNode, 0);
                        }
                        fenbuAllocateMap.put(siteNode, siteVaildAllocate + fenbuAllocateMap.get(siteNode));
                        clientDemandBandwidth -= siteVaildAllocate;
                    }
                }

                int febuAllCost = 0;
                if (clientDemandBandwidth > 0) {
                    //成本为0的情况已经没有了，剩余的只能硬插，计算下最小成本
                    List<Integer> costlist = new ArrayList<>();
                    for (SiteNode siteNode : siteNodeSet) {
                        if (!siteCost.containsKey(siteNode.getName())) {
                            //需要用到以往成本而不存在，先初始化为0
                            siteCost.put(siteNode.getName(), 0);
                        }
                        int currentBandwidth = siteNode.getCurrentBandwidth();
                        int siteVaildAllocate = Math.min(currentBandwidth, clientDemandBandwidth);
                        int preCost = siteCost.get(siteNode.getName()) + siteVaildAllocate;
                        int changeCost = AllocatedCollection.computeCost(siteNode.getName(), preCost);
                        costlist.add(changeCost);
                    }

                    List<SiteNode> sitenodelost = new ArrayList<>(siteNodeSet);
                    while (clientDemandBandwidth > 0) {
                        int min = Integer.MAX_VALUE;
                        int pos = 0;
                        for (int i = 0; i < costlist.size(); i++) {
                            if (costlist.get(i) < min) {
                                min = costlist.get(i);
                                pos = i;
                            }
                        }

                        SiteNode siteNode = sitenodelost.get(pos);
                        int currentBandwidth = siteNode.getCurrentBandwidth();
                        int siteVaildAllocate = Math.min(currentBandwidth, clientDemandBandwidth);
                        int preCost = siteCost.get(siteNode.getName()) + siteVaildAllocate;
                        int changeCost = AllocatedCollection.computeCost(siteNode.getName(), preCost);
                        febuAllCost += changeCost;
                        clientDemandBandwidth -= siteVaildAllocate;
                        if (!fenbuAllocateMap.containsKey(siteNode)) {
                            fenbuAllocateMap.put(siteNode, 0);
                        }
                        siteNode.setCurrentBandwidth(siteNode.getCurrentBandwidth() - siteVaildAllocate);
                        //本次用户需求中，某节点的总成本增加
                        siteCost.put(siteNode.getName(), siteCost.get(siteNode.getName()) + siteVaildAllocate);
                        fenbuAllocateMap.put(siteNode, siteVaildAllocate + fenbuAllocateMap.get(siteNode));
                        costlist.remove(pos);
                        sitenodelost.remove(pos);
                    }

                }

                // if (minCost < febuAllCost && findInsertLowCost) {
                //     selectSiteNode.setCurrentBandwidth(selectSiteNode.getCurrentBandwidth() - selectCost);
                //     //本次用户需求中，某节点的总成本增加
                //     siteCost.put(selectSiteNode.getName(), siteCost.get(selectSiteNode.getName()) + selectCost);
                //     allocationMap.put(selectSiteNode.getName(), selectCost);
                //     allocationMaps.put(clientNode.getName(), allocationMap);
                // } else {
                for (Map.Entry<SiteNode, Integer> entry : fenbuAllocateMap.entrySet()) {
                    SiteNode siteNode = entry.getKey();
                    // siteNode.setCurrentBandwidth(siteNode.getCurrentBandwidth() - entry.getValue());
                    // //本次用户需求中，某节点的总成本增加
                    // siteCost.put(siteNode.getName(), siteCost.get(siteNode.getName()) + entry.getValue());
                    allocationMap.put(siteNode.getName(), entry.getValue());
                    // OptimizeResult optimizeResult = new OptimizeResult();
                    // optimizeResult.redistribution(allocationMap, graph);
                    allocationMaps.put(clientNode.getName(), allocationMap);
                }
                //OptimizeResult optimizeResult = new OptimizeResult();
                //optimizeResult.redistribution(allocationMap, graph);

                //}

                Integer all = 0;
                for (Map.Entry<String, Integer> entry : allocationMap.entrySet()) {
                    if (entry.getValue() > graph.getSiteIdMap().get(entry.getKey()).getTotalBandwidth())
                        System.out.println(clientNode.getName() + "erorrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr");
                    all += entry.getValue();
                }

                if (!all.equals(demandMap.get(clientNode.getName()))) {
                    System.out.println(clientNode.getName() + "erorrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrr");
                }
            }



            for (Map.Entry<String, Integer> entry : siteCost.entrySet()) {
                AllocatedCollection.addNewCost(entry.getKey(), entry.getValue());
            }

            ResultCompute.store(new HashMap<>(allocationMaps));
            ResultCompute.compute();
            demand.setDistributionSuccess(true);
            TimestampResult timestampResult = new TimestampResult(demand.getTimestamp(), new HashMap<>(allocationMaps));
            ResultHandle.timestampResults.add(timestampResult);
        }
    }

    private void reloadBandwidth() {
        for (SiteNode siteNode : graph.getSiteNodes()) {
            siteNode.setCurrentBandwidth(siteNode.getTotalBandwidth());
        }
        allocationMaps.clear();
    }
}
